//////////////////////////////////////////////////////////////////////
//
//	ZETeam source code 
//	Copyright (c) ZETeam 2011-..
//	
//	File: CRotation.cpp
//  Description: Rotation classes. Based on Id Software source code.
// 
//	History:
//	- February 27, 2011: Created by Max Streltsov
//
//////////////////////////////////////////////////////////////////////

#ifndef CROTATION_H
#define CROTATION_H

#include "CVector.h"
#include "CMatrix.h"

/*
===============================================================================

	Describes a complete rotation in degrees about an abritray axis.
	A local rotation matrix is stored for fast rotation of multiple points.

===============================================================================
*/


class CAngles;
class CQuat;

class CRotation {

	friend class CAngles;
	friend class CQuat;
	friend class CMat3;

public:
						CRotation( void );
						CRotation( const CVec3 &rotationOrigin, const CVec3 &rotationVec, const float rotationAngle );

	void				set( const CVec3 &rotationOrigin, const CVec3 &rotationVec, const float rotationAngle );
	void				setOrigin( const CVec3 &rotationOrigin );
	void				setVec( const CVec3 &rotationVec );					// has to be normalized
	void				setVec( const float x, const float y, const float z );	// has to be normalized
	void				setAngle( const float rotationAngle );
	void				scale( const float s );
	void				reCalculateMatrix( void );
	const CVec3 &		getOrigin( void ) const;
	const CVec3 &		getVec( void ) const;
	float				getAngle( void ) const;

	CRotation			operator-() const;										// flips rotation
	CRotation			operator*( const float s ) const;						// scale rotation
	CRotation			operator/( const float s ) const;						// scale rotation
	CRotation &		operator*=( const float s );							// scale rotation
	CRotation &		operator/=( const float s );							// scale rotation
	CVec3				operator*( const CVec3 &v ) const;						// rotate vector

	friend CRotation	operator*( const float s, const CRotation &r );		// scale rotation
	friend CVec3		operator*( const CVec3 &v, const CRotation &r );		// rotate vector
	friend CVec3 &		operator*=( CVec3 &v, const CRotation &r );			// rotate vector

	CAngles			toAngles( void ) const;
	CQuat				toQuat( void ) const;
	const CMat3 &		toMat3( void ) const;
	CMat4				toMat4( void ) const;
	CVec3				toAngularVelocity( void ) const;

	void				rotatePoint( CVec3 &point ) const;

	void				normalize180( void );
	void				normalize360( void );

private:
	CVec3				origin;			// origin of rotation
	CVec3				vec;			// normalized vector to rotate around
	float				angle;			// angle of rotation in degrees
	mutable CMat3		axis;			// rotation axis
	mutable bool		axisValid;		// true if rotation axis is valid
};


Z_INLINE CRotation::CRotation( void ) {
}

Z_INLINE CRotation::CRotation( const CVec3 &rotationOrigin, const CVec3 &rotationVec, const float rotationAngle ) {
	origin = rotationOrigin;
	vec = rotationVec;
	angle = rotationAngle;
	axisValid = false;
}

Z_INLINE void CRotation::set( const CVec3 &rotationOrigin, const CVec3 &rotationVec, const float rotationAngle ) {
	origin = rotationOrigin;
	vec = rotationVec;
	angle = rotationAngle;
	axisValid = false;
}

Z_INLINE void CRotation::setOrigin( const CVec3 &rotationOrigin ) {
	origin = rotationOrigin;
}

Z_INLINE void CRotation::setVec( const CVec3 &rotationVec ) {
	vec = rotationVec;
	axisValid = false;
}

Z_INLINE void CRotation::setVec( float x, float y, float z ) {
	vec[0] = x;
	vec[1] = y;
	vec[2] = z;
	axisValid = false;
}

Z_INLINE void CRotation::setAngle( const float rotationAngle ) {
	angle = rotationAngle;
	axisValid = false;
}

Z_INLINE void CRotation::scale( const float s ) {
	angle *= s;
	axisValid = false;
}

Z_INLINE void CRotation::reCalculateMatrix( void ) {
	axisValid = false;
	toMat3();
}

Z_INLINE const CVec3 &CRotation::getOrigin( void ) const {
	return origin;
}

Z_INLINE const CVec3 &CRotation::getVec( void ) const  {
	return vec;
}

Z_INLINE float CRotation::getAngle( void ) const  {
	return angle;
}

Z_INLINE CRotation CRotation::operator-() const {
	return CRotation( origin, vec, -angle );
}

Z_INLINE CRotation CRotation::operator*( const float s ) const {
	return CRotation( origin, vec, angle * s );
}

Z_INLINE CRotation CRotation::operator/( const float s ) const {
	assert( s != 0.0f );
	return CRotation( origin, vec, angle / s );
}

Z_INLINE CRotation &CRotation::operator*=( const float s ) {
	angle *= s;
	axisValid = false;
	return *this;
}

Z_INLINE CRotation &CRotation::operator/=( const float s ) {
	assert( s != 0.0f );
	angle /= s;
	axisValid = false;
	return *this;
}

Z_INLINE CVec3 CRotation::operator*( const CVec3 &v ) const {
	if ( !axisValid ) {
		toMat3();
	}
	return ((v - origin) * axis + origin);
}

Z_INLINE CRotation operator*( const float s, const CRotation &r ) {
	return r * s;
}

Z_INLINE CVec3 operator*( const CVec3 &v, const CRotation &r ) {
	return r * v;
}

Z_INLINE CVec3 &operator*=( CVec3 &v, const CRotation &r ) {
	v = r * v;
	return v;
}

Z_INLINE void CRotation::rotatePoint( CVec3 &point ) const {
	if ( !axisValid ) {
		toMat3();
	}
	point = ((point - origin) * axis + origin);
}

#endif /* CROTATION_H*/
